/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2014年4月20日
 * <修改描述:>
 */
package com.tx.core.method.resolver.impl;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.Conventions;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;

import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.method.annotation.MethodParamBody;
import com.tx.core.method.container.NativeInvokeContainer;
import com.tx.core.method.databinder.NativeDataBinderFactory;
import com.tx.core.method.exceptions.MethodArgResolveBindException;
import com.tx.core.method.request.NativeInvokeRequest;
import com.tx.core.method.resolver.NativeMethodArgumentResolver;

/**
 * 参数具@EventListenerParam注解的方法参数解析器<br/>
 * <功能详细描述>
 * 
 * @author  Administrator
 * @version  [版本号, 2014年4月20日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class MethodParamBodyMethodArgumentResolver
        implements NativeMethodArgumentResolver {
    
    /** 日志记录器 */
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    
    /** <默认构造函数> */
    public MethodParamBodyMethodArgumentResolver() {
    }
    
    /** order */
    @Override
    public int getOrder() {
        return 0;
    }
    
    /**
     * @param parameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return (parameter.hasParameterAnnotation(MethodParamBody.class)
                && !BeanUtils.isSimpleProperty(parameter.getParameterType()));
    }
    
    /**
     * @param parameter
     * @param request
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter parameter,
            NativeInvokeContainer container, NativeInvokeRequest invokeRequest,
            NativeDataBinderFactory dataBinderFactory) throws Exception {
        //获取参数
        parameter = parameter.nestedIfOptional();
        //获取参数名
        String name = getNameForParameter(parameter);
        
        //创建对象实体
        Object obj = createAttribute(name, parameter, invokeRequest);
        DataBinder binder = null;
        if (dataBinderFactory != null) {
            dataBinderFactory.createBinder(invokeRequest, obj, name);
        } else {
            binder = new DataBinder(obj, name);
        }
        binder.bind(new MutablePropertyValues(
                getParametersStartingWith(invokeRequest, name + ".")));
        
        if (binder.getTarget() != null) {
            if (binder.getBindingResult().hasErrors()) {
                throw new MethodArgResolveBindException(
                        binder.getBindingResult());
            }
        }
        
        return binder.convertIfNecessary(binder.getTarget(),
                parameter.getParameterType(),
                parameter);
    }
    
    /**
     * 创建对象<br/>
     * <功能详细描述>
     * @param attributeName
     * @param parameter
     * @param invokeRequest
     * @return
     * @throws Exception [参数说明]
     * 
     * @return Object [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private Object createAttribute(String attributeName,
            MethodParameter parameter, NativeInvokeRequest invokeRequest)
            throws Exception {
        return BeanUtils.instantiateClass(parameter.getParameterType());
    }
    
    /**
     * 获取方法参数名<br/>
     * <功能详细描述>
     * @param parameter
     * @return [参数说明]
     * 
     * @return String [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private static String getNameForParameter(MethodParameter parameter) {
        MethodParamBody ann = parameter
                .getParameterAnnotation(MethodParamBody.class);
        String name = (ann != null ? ann.value() : null);
        return (StringUtils.hasText(name) ? name
                : Conventions.getVariableNameForParameter(parameter));
    }
    
    /**
     * 获取参数集合<br/>
     * <功能详细描述>
     * @param request
     * @param prefix
     * @return [参数说明]
     * 
     * @return Map<String,Object[]> [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private static Map<String, Object[]> getParametersStartingWith(
            NativeInvokeRequest request, String prefix) {
        AssertUtils.notNull(request, "Request must not be null");
        
        Iterator<String> paramNames = request.getParameterNames();
        Map<String, Object[]> params = new TreeMap<String, Object[]>();
        if (prefix == null) {
            prefix = "";
        }
        while (paramNames != null && paramNames.hasNext()) {
            String paramName = paramNames.next();
            if ("".equals(prefix) || paramName.startsWith(prefix)) {
                String unprefixed = paramName.substring(prefix.length());
                
                Object[] values = request.getParameterValues(paramName);
                if (values == null || values.length == 0) {
                    continue;
                }
                params.put(unprefixed, values);
            }
        }
        return params;
    }
    
    /**
     * 如果必要的话对参数进行适配<br/>
     * <功能详细描述>
     * @param arg
     * @param parameter
     * @return [参数说明]
     * 
     * @return Object [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    @Nullable
    protected Object adaptArgumentIfNecessary(@Nullable Object arg,
            MethodParameter parameter) {
        if (parameter.getParameterType() == Optional.class) {
            if (arg == null
                    || (arg instanceof Collection
                            && ((Collection<?>) arg).isEmpty())
                    || (arg instanceof Object[]
                            && ((Object[]) arg).length == 0)) {
                return Optional.empty();
            } else {
                return Optional.of(arg);
            }
        }
        return arg;
    }
}